using System;
using System.Collections.Generic;
using System.Text;
using WinTestBinding;
using System.Net;
using Newtonsoft.Json;
using System.IO;

namespace UTorrentLib
{
    public class TorrentSession
    {
        private String m_strUsername, m_strHostname, m_strPassword;
        private int m_iPort;
        private String m_strLastCache = String.Empty;
        private UTorrentList m_TorrentsFinished, m_TorrentsDownloading, m_TorrentsSeeding, m_TorrentsAll;
        public UTorrentList TorrentsFinished
        {
            get
            {
                return m_TorrentsFinished;
            }
        }
        public UTorrentList TorrentsDownloading
        {
            get
            {
                return m_TorrentsDownloading;
            }
        }
        public UTorrentList TorrentsSeeding
        {
            get
            {
                return m_TorrentsSeeding;
            }
        }
        public UTorrentList TorrentsAll
        {
            get
            {
                return m_TorrentsAll;
            }
        }

        public long DownloadSpeed
        {
            get
            {
                return CalcDownloadSpeed();
            }
        }
        public long UploadSpeed
        {
            get
            {
                return CalcUploadSpeed();
            }
        }

        #region Formatted string for displaying in a Gui.
        public String fUploadSpeed
        {
            get
            {
                return DisplayFunctions.FormatSize(this.UploadSpeed) + "/s";
            }
        }
        public String fDownloadSpeed
        {
            get
            {
                return DisplayFunctions.FormatSize(this.DownloadSpeed) + "/s";
            }
        }
        #endregion
        public TorrentSession(String Hostname, String Username, String Password, int Port)
        {
            this.m_strHostname = Hostname;
            this.m_strUsername = Username;
            this.m_strPassword = Password;
            this.m_iPort = Port;

            m_TorrentsAll = new UTorrentList();
            m_TorrentsDownloading = new UTorrentList();
            m_TorrentsFinished = new UTorrentList();
            m_TorrentsSeeding = new UTorrentList();
        }


        public void Initialize()
        {
            JavaScriptObject GuiResp = GetResponseFromUi("?list=1");
            JavaScriptArray tt = (JavaScriptArray)(GuiResp["torrents"]);
            this.m_strLastCache = (String)((JavaScriptObject)GuiResp)["torrentc"];

            for (int i = 0; i < tt.Count; i++)
            {
                UTorrent tmpTorrent = new UTorrent((JavaScriptArray)tt[i],this);
                if (((tmpTorrent.Status & UTorrentStatus.Started) == UTorrentStatus.Started))
                {
                    if (tmpTorrent.Progress != 100)
                        m_TorrentsDownloading.Add(tmpTorrent);
                    else
                        m_TorrentsSeeding.Add(tmpTorrent);
                }
                else
                {
                    m_TorrentsFinished.Add(tmpTorrent);
                }
                m_TorrentsAll.Add(tmpTorrent);
            }
        }
        public void UpdateLists()
        {

            //Sending request and retrieving response.
            JavaScriptObject GuiResp = GetResponseFromUi("?list=1&cid=" + this.m_strLastCache);
            this.m_strLastCache = (String)GuiResp["torrentc"];
            //Creating a List of the modified torrents.
            List<JavaScriptArray> mTorrents = new List<JavaScriptArray>();
            if (GuiResp.ContainsKey("torrentp"))
            {
                JavaScriptArray mjsTorrents = (JavaScriptArray)GuiResp["torrentp"];
                for (int i = 0; i < mjsTorrents.Count; i++)
                {
                    mTorrents.Add((JavaScriptArray)mjsTorrents[i]);
                }
            }

            //Creating a List of removed Hashes.
            List<String> rHashes = new List<string>();
            if (GuiResp.ContainsKey("torrentm"))
            {
                JavaScriptArray rjsHashes = (JavaScriptArray)GuiResp["torrentm"];
                for (int i = 0; i < rjsHashes.Count; i++)
                {
                    rHashes.Add((String)rjsHashes[i]);
                }
            }

            // Applying modifications for the modified Torrents.
            bool found;
            for (int i = 0; i < mTorrents.Count; i++)
            {
                found = false;
                #region First look in the downloading Torrents.
                for (int j = 0; j < this.m_TorrentsDownloading.Count; j++)
                {
                    #region modified torrent was a downloading torrent.
                    if ((String)mTorrents[i][0] == this.m_TorrentsDownloading[j].Hash)
                    {
                        //Still downloading update.
                        if ((long)mTorrents[i][4] != 1000)
                        {
                            this.m_TorrentsDownloading[j].m_jsTorrent = mTorrents[i];
                        }
                        else
                        {
                            UTorrent oldTorrent = this.m_TorrentsDownloading[j];
                            this.m_TorrentsDownloading.RemoveAt(j);
                            oldTorrent.m_jsTorrent = mTorrents[i];

                            // Seeding.
                            if (((UTorrentStatus)((long)mTorrents[i][1])& UTorrentStatus.Started) == UTorrentStatus.Started)
                            {
                                this.m_TorrentsSeeding.Add(oldTorrent);
                            }
                            // Finished.
                            else
                            {
                                this.m_TorrentsFinished.Add(oldTorrent);
                            }
                        }
                        found = true;
                        break;
                    }
                    #endregion
                }
                if (found)
                {
                    continue;
                }
                #endregion
                #region looking in the Seeding torrents.
                for (int j = 0; j < this.m_TorrentsSeeding.Count; j++)
                {
                    //It was a Seeding torrent
                    if ((String)mTorrents[i][0] == this.m_TorrentsSeeding[j].Hash)
                    {

                        //Still seeding
                        if (((UTorrentStatus)((long)mTorrents[i][1]) & UTorrentStatus.Started) == UTorrentStatus.Started)
                        {
                            this.m_TorrentsSeeding[j].m_jsTorrent = mTorrents[i];
                        }
                        //finished.
                        else
                        {
                            UTorrent oldTorrent = this.m_TorrentsSeeding[j];
                            oldTorrent.m_jsTorrent =  mTorrents[i];
                            this.m_TorrentsSeeding.RemoveAt(j);
                            this.m_TorrentsFinished.Add(oldTorrent);
                        }
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                #endregion
                #region Look in the finished/stopped torrents
                for (int j = 0; j < this.m_TorrentsFinished.Count; j++)
                {
                    if ((String)mTorrents[i][0] == this.m_TorrentsFinished[j].Hash)
                    {
                        if (((UTorrentStatus)((long)mTorrents[i][1]) & UTorrentStatus.Started) == UTorrentStatus.Started)
                        {
                            UTorrent oldTorrent = this.m_TorrentsFinished[j];
                            oldTorrent.m_jsTorrent =  mTorrents[i];
                            this.m_TorrentsFinished.RemoveAt(j);

                            if ((long)mTorrents[i][4] == 1000)
                            {
                                this.m_TorrentsSeeding.Add(oldTorrent);
                            }
                            else
                            {
                                this.m_TorrentsDownloading.Add(oldTorrent);
                            }
                        }
                        else
                        {
                           this.m_TorrentsFinished[j].m_jsTorrent = mTorrents[i];
                        }
                        found = true;
                        break;
                    }
                }
                #endregion
                #region Nouveau Torrent
                if (!found)
                {
                    UTorrent NewTorrent = new UTorrent(mTorrents[i],this);
                    if ((NewTorrent.Status & UTorrentStatus.Started) == UTorrentStatus.Started)
                    {
                        if ((long)mTorrents[i][4] == 1000)
                        {
                            this.m_TorrentsSeeding.Add(NewTorrent);
                        }
                        else
                        {
                            this.m_TorrentsDownloading.Add(NewTorrent);
                        }
                    }
                    else
                    {
                        this.m_TorrentsFinished.Add(NewTorrent);
                    }
                    this.m_TorrentsAll.Add(NewTorrent);
                }
                #endregion
            }
            for (int i = 0; i < rHashes.Count; i++)
            {
                String hash = rHashes[i];
                found = false;
                for (int j = 0; j < this.TorrentsAll.Count; j++)
                {
                    if (TorrentsAll[j].Hash == hash)
                    {
                        TorrentsAll.RemoveAt(j);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                for (int j = 0; j < this.TorrentsDownloading.Count; j++)
                {
                    if (TorrentsDownloading[j].Hash == hash)
                    {
                        TorrentsDownloading.RemoveAt(j);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                for (int j = 0; j < this.TorrentsFinished.Count; j++)
                {
                    if (TorrentsFinished[j].Hash == hash)
                    {
                        TorrentsFinished.RemoveAt(j);
                        found = true;
                        break;
                    }
                }
                if (found) continue;
                for (int j = 0; j < this.TorrentsSeeding.Count; j++)
                {
                    if (TorrentsSeeding[j].Hash == hash)
                    {
                        TorrentsSeeding.RemoveAt(j);
                        found = true;
                        break;
                    }
                }
            }
        }

        public JavaScriptObject GetResponseFromUi(String request)
        {
            HttpWebResponse resp = null;
            HttpWebRequest ts = (HttpWebRequest)HttpWebRequest.Create("http://" + this.m_strHostname + ":" + this.m_iPort.ToString() + "/gui/" + request);
            ts.KeepAlive = false;
            ts.Headers.Add("Authorization: Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(this.m_strUsername + ":" + this.m_strPassword)));
            resp = (HttpWebResponse)ts.GetResponse();
            StreamReader responseReader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
            String response = responseReader.ReadToEnd();

            Object GuiResp = JavaScriptConvert.DeserializeObject(response);
            return (JavaScriptObject)GuiResp;
        }

        public bool AddTorrentFromURL(Uri url)
        {
            try
            {
                JavaScriptObject GuiResp = GetResponseFromUi("?action=add-url&s=" + System.Web.HttpUtility.UrlEncode(url.AbsoluteUri));
                return true;
            }
            catch
            {
                return false;
            }
        }

        private long CalcDownloadSpeed()
        {
            long returned = 0;
            for (int i = 0; i < this.m_TorrentsAll.Count; i++)
            {
                returned += this.m_TorrentsAll[i].DownloadSpeed;
            }
            return returned;
        }
        private long CalcUploadSpeed()
        {
            long result = 0;
            for (int i = 0; i < this.m_TorrentsAll.Count; i++)
            {
                result += this.m_TorrentsAll[i].UploadSpeed;
            }
            return result;
        }           
    }
}
